package br.edu.ufcg.splab.util;

import java.util.ArrayList;
import java.util.List;

import br.edu.ufcg.splab.core.InterfaceEdge;
import br.edu.ufcg.splab.core.InterfaceVertex;
import br.edu.ufcg.splab.core.graph.Graph;

public class AnalyseSpecifications {

	private Graph graph;
	
	public AnalyseSpecifications(Graph graph){
		this.graph = graph;
	}
	
	public int getNumOfForks(){	
		int numOfVertexWithForks = 0;
		for(InterfaceVertex vertex : graph.getStates().values()){	
			if(vertex.getOutTransitions().size() > 1 ){ 
				numOfVertexWithForks += 1;
		    }   
		}
		return numOfVertexWithForks;
	}
	
	public int getNumOfJoins(){	
		int numOfVertexWithJoins = 0;
		for(InterfaceVertex vertex : graph.getStates().values()){
			if(vertex.getInTransitions().size() > 1 ){
				numOfVertexWithJoins += 1;  
				//System.out.print(vertex.getLabel() + "(" + vertex.getInTransitions().size()+ ") - ");
		    }  
		}
		//System.out.println();
		return numOfVertexWithJoins;
	}
	
	public int getNumOfTransitionsOfForks(){
		int numOfTransitionsOfForks = 0;
		for(InterfaceVertex vertex : graph.getStates().values()){
			if(vertex.getOutTransitions().size() > 1 ){ 
				numOfTransitionsOfForks += vertex.getOutTransitions().size();
		    }   
		}
		return numOfTransitionsOfForks;
	}
	
	public int getNumOfTransitionsOfJoins(){ 
		int numOfTransitionsOfJoins = 0;
		for(InterfaceVertex vertex : graph.getStates().values()){
			if(vertex.getInTransitions().size() > 1 ){ 
				numOfTransitionsOfJoins += vertex.getInTransitions().size();  
		    }  
		}
		return numOfTransitionsOfJoins;
	}
	
	public int getNumOfPathsWithLoops(){
		List<InterfaceVertex> visitedsVertex = new ArrayList<InterfaceVertex>();	
		int numLoops = 0;
		visitedsVertex.add(graph.getRoot());
		numLoops = dfsSaveNumOfLoopsGraph(graph.getRoot(), visitedsVertex);
		return numLoops;
	}	
		
	private boolean containsVertex(List<InterfaceVertex> list, InterfaceVertex vertex){
		for (InterfaceVertex interfaceVertex : list) {
			if(interfaceVertex.getLabel().equals(vertex.getLabel())){
				return true;
			}
		}
		return false;
	}
	
	private int dfsSaveNumOfLoopsGraph(InterfaceVertex vertex, List<InterfaceVertex> visitedsVertex){
		int numLoops = 0;
		if(!vertex.isLeaf()) {
			for (InterfaceEdge edge : vertex.getOutTransitions()) {
				InterfaceVertex vertexTo = edge.getTo();
				if(vertex.getLabel().equals(vertexTo.getLabel())){
					numLoops = numLoops + 1;
//					for(InterfaceVertex v : visitedsVertex){
//						System.out.print(v.getLabel() + ", ");
//					}
//					System.out.println(vertexTo.getLabel());
				}else if (!containsVertex(visitedsVertex, vertexTo)) {					
					visitedsVertex.add(vertexTo);					
					numLoops = numLoops + dfsSaveNumOfLoopsGraph(vertexTo, visitedsVertex);
					visitedsVertex.remove(vertexTo);			
				} else {
					numLoops = numLoops + 1;
//					for(InterfaceVertex v : visitedsVertex){
//						System.out.print(v.getLabel() + ", ");
//					}
//					System.out.println(vertexTo.getLabel());
				}
			}
		}
		return numLoops;
	}
	
	public List<String> getPathsWithLoops(){
		List<InterfaceVertex> visitedsVertex = new ArrayList<InterfaceVertex>();	
		visitedsVertex.add(graph.getRoot());
		List<String> paths = new ArrayList<String>();
		return dfsSavePathsOfLoopsGraph(graph.getRoot(), visitedsVertex, paths);
	}	
		
	
	private List<String> dfsSavePathsOfLoopsGraph(InterfaceVertex vertex, List<InterfaceVertex> visitedsVertex, List<String> paths){
		
		if(!vertex.isLeaf()) {
			for (InterfaceEdge edge : vertex.getOutTransitions()) {
				InterfaceVertex vertexTo = edge.getTo();
				if(vertex.getLabel().equals(vertexTo.getLabel())){
					String p = "";
					for(InterfaceVertex v : visitedsVertex){
						p = p + v.getLabel() + ", ";
					}
					p = p + vertexTo.getLabel();
					paths.add(p);
				}else if (!containsVertex(visitedsVertex, vertexTo)) {					
					visitedsVertex.add(vertexTo);					
					dfsSavePathsOfLoopsGraph(vertexTo, visitedsVertex, paths);
					visitedsVertex.remove(vertexTo);			
				} else {
					String p = "";
					for(InterfaceVertex v : visitedsVertex){
						p = p + v.getLabel() + ", ";
					}
					p = p + vertexTo.getLabel();
					paths.add(p);
				}
			}
		}
		return paths;
	}	
	
	public static void main(String[] args) {			
		Graph graph = null;
		ReadTGF readTGF = new ReadTGF();	
		
		
		try {
			graph = readTGF.getGraph("./test-data/failures_identifier/defect_model/UC_CB01-annotated-ofuscado.tgf");
			AnalyseSpecifications analyse = new AnalyseSpecifications(graph);
			System.out.println("Number of states: " + graph.getStates().size());
			System.out.println("Number of transitions: " + graph.getEdges().size());			
			System.out.println("Number of forks: " + analyse.getNumOfForks());
			System.out.println("Number of transitions of forks: " + analyse.getNumOfTransitionsOfForks());
			System.out.println("Number of joins: " + analyse.getNumOfJoins());
			System.out.println("Number of transitions of joins: " + analyse.getNumOfTransitionsOfJoins());
			System.out.println("Number of paths with loops: " + analyse.getNumOfPathsWithLoops());
			
			for(String s: analyse.getPathsWithLoops()){
				System.out.println(s);
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		
	}
}
